diff --git a/hw/kdrive/linux/tslib.c b/hw/kdrive/linux/tslib.c
index c4caff9..3b9174b 100644
--- a/hw/kdrive/linux/tslib.c
+++ b/hw/kdrive/linux/tslib.c
@@ -100,8 +100,6 @@
 {
     KdMouseInfo	    *mi = closure;
     struct ts_sample event;
-    long	    x, y;
-    unsigned long   flags;
 
     if (tslib_raw_event_hook)
       {
@@ -116,14 +114,107 @@
 	return;
       }
 
+#define SCROLL_TIMEOUT   200 /* after this many ms of holding, we drag, not scroll */
+#define SCROLL_RSQ       400 /* sqrt(thismuch) pixel motion triggers scrolling */
+#define SCROLL_THRESHOLD 8   /* Once we're scrolling, need to move at least this many pixels to react */
     while (ts_read(tsDev, &event, 1) == 1)
       {
-	flags = (event.pressure) ? KD_BUTTON_1 : 0;
-	x = event.x;
-	y = event.y;
-	
-	KdEnqueueMouseEvent (mi, flags, x, y);
+	// click/drag if
+	// 1. release event comes before timeout AND we stayed in a small area.
+	//    In this case press, release events are both sent at release
+	// 2. release event comes after the timeout.
+	//    In this case, press comes at timeout, release comes at release
+	// Scroll happens in all other cases. These are large, fast motions:
+	// 1. release event comes before timeout, but there was
+	// considerable motion.
+	//    In this case, the scrolling starts at the original press
+	//    location, as soon as we leave the area
+	//
+	// In other words:
+	// 1. As soon as we leave the area, scroll
+	// 2. As soon as we timeout, drag
+	// 3. If we release before #1 or #2 happen, click
+	// 4. No events before one of these occurs
+	static int pressedNow = 0;
+	static int wherePressedX, wherePressedY;
+	static int scrollLastX, scrollLastY;
+	static CARD32 whenPressed;
+	static int scrolling, dragging;
+	if( event.pressure )
+	{
+	  if( !pressedNow )
+	  {
+	    // first press
+	    whenPressed = GetTimeInMillis();
+	    wherePressedX = scrollLastX = event.x;
+	    wherePressedY = scrollLastY = event.y;
+	    scrolling = dragging = FALSE;
+	  }
+	  else
+	  {
+	    // still pressed
+	    if(!scrolling && !dragging)
+	    {
+	      if(GetTimeInMillis() - whenPressed > SCROLL_TIMEOUT)
+		dragging = TRUE;
+	      else
+	      {
+		unsigned int dx = event.x - wherePressedX;
+		unsigned int dy = event.y - wherePressedY;
+		if(dx*dx + dy*dy > SCROLL_RSQ)
+		  scrolling = TRUE;
+	      }
+	    }
+	    if(scrolling)
+	    {
+	      int dx  = event.x - scrollLastX;
+	      int dy  = event.y - scrollLastY;
+	      unsigned int dx2 = dx*dx;
+	      unsigned int dy2 = dy*dy;
+
+	      if(dx2 > SCROLL_THRESHOLD*SCROLL_THRESHOLD ||
+		 dy2 > SCROLL_THRESHOLD*SCROLL_THRESHOLD)
+	      {
+		if( dy2 >= dx2 )
+		{
+		  if(dy > 0)
+		    KdEnqueueMouseEvent (mi, KD_BUTTON_4, wherePressedX, wherePressedY);
+		  else
+		    KdEnqueueMouseEvent (mi, KD_BUTTON_5, wherePressedX, wherePressedY);
+		}
+		else
+		{
+		  if(dx > 0)
+		    KdEnqueueMouseEvent (mi, KD_BUTTON_6, wherePressedX, wherePressedY);
+		  else
+		    KdEnqueueMouseEvent (mi, KD_BUTTON_7, wherePressedX, wherePressedY);
+		}
+		KdEnqueueMouseEvent (mi, 0, wherePressedX, wherePressedY);
+
+		scrollLastX = event.x;
+		scrollLastY = event.y;
+	      }
+
+	    }
+	    else if(dragging)
+	    {
+		KdEnqueueMouseEvent (mi, KD_BUTTON_1, event.x, event.y);
+	    }
+
+	  }
+	}
+	else
+	{
+	  // release event
+	  // If we haven't sent a press event yet, do that
+	  if(!scrolling && !dragging)
+	    KdEnqueueMouseEvent (mi, KD_BUTTON_1, event.x, event.y);
+	  if(!scrolling)
+	    KdEnqueueMouseEvent (mi, 0, event.x, event.y);
+	}
+
+	pressedNow = event.pressure;
       }
 }
 
 
diff --git a/hw/kdrive/src/kdrive.h b/hw/kdrive/src/kdrive.h
index 04078a0..3259fbc 100644
--- a/hw/kdrive/src/kdrive.h
+++ b/hw/kdrive/src/kdrive.h
@@ -728,6 +728,8 @@ KdEnqueueKeyboardEvent(unsigned char	scan_code,
 #define KD_BUTTON_3	0x04
 #define KD_BUTTON_4	0x08
 #define KD_BUTTON_5	0x10
+#define KD_BUTTON_6	0x20
+#define KD_BUTTON_7	0x40
 #define KD_MOUSE_DELTA	0x80000000
 
 void
diff --git a/hw/kdrive/src/kinput.c b/hw/kdrive/src/kinput.c
index a655181..f1f4db2 100644
--- a/hw/kdrive/src/kinput.c
+++ b/hw/kdrive/src/kinput.c
@@ -1484,7 +1484,7 @@ KdEnqueueMouseEvent(KdMouseInfo *mi, unsigned long flags, int rx, int ry)
 
     buttons = flags;
 
-    for (button = KD_BUTTON_1, n = 0; button <= KD_BUTTON_5; button <<= 1, n++)
+    for (button = KD_BUTTON_1, n = 0; button <= KD_BUTTON_7; button <<= 1, n++)
     {
 	if ((mi->buttonState & button) ^ (buttons & button))
 	{
